home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / Other Langs / Tickle-4.0 (tcl) / tcl / expecTerm / inter_select.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-11-17  |  24.6 KB  |  858 lines  |  [TEXT/MPS ]

  1. /* interact (using select) - give user keyboard control
  2.  
  3. Written by: Don Libes, NIST, 2/6/90
  4.  
  5. Design and implementation of this program was paid for by U.S. tax
  6. dollars.  Therefore it is public domain.  However, the author and NIST
  7. would appreciate credit if this program or parts of it are used.
  8.  
  9. $Revision: 1.2 $
  10. $Date: 1992/02/21 18:47:50 $
  11.  
  12. */
  13. /* #define DEBUG_PROC_WRITE 1 */
  14. #include <stdio.h>
  15. #include <sys/types.h>
  16. #include <sys/time.h>
  17. #include <sys/ioctl.h>
  18. #include "expterm.h"
  19. #include <errno.h>
  20. #ifdef EXTERN_ERRNO
  21. extern int errno;
  22. #endif
  23. #include <signal.h>
  24. #include <ctype.h>
  25. #ifndef NO_STRING_H
  26. #include <string.h>
  27. #endif
  28. #ifdef HPUX
  29. #include <unistd.h>
  30. #include <sys/ptyio.h>
  31. #endif
  32. #ifdef _AIX
  33. /* AIX has some unusual definition of FD_SET */
  34. #include <sys/select.h>
  35. #endif
  36. #include <tcl.h>
  37. #include "translate.h"
  38. #include "global.h"
  39. #include "command.h"
  40.  
  41. #define min(x,y)    (((x)<(y))?(x):(y))
  42.  
  43. #ifndef FD_SET
  44. #define FD_SET(fd,fdset)    (fdset)->fds_bits[0] |= (1<<(fd))
  45. #define FD_CLR(fd,fdset)    (fdset)->fds_bits[0] &= ~(1<<(fd))
  46. #define FD_ZERO(fdset)        (fdset)->fds_bits[0] = 0
  47. #define FD_ISSET(fd,fdset)    (((fdset)->fds_bits[0]) & (1<<(fd)))
  48. #ifndef AUX2
  49. typedef struct fd_set {
  50.     long fds_bits[1];
  51.     /* any implementation so pathetic as to not define FD_SET will just */
  52.     /* have to suffer with only 32 bits worth of fds */
  53. } fd_set;
  54. #endif
  55. #endif
  56.  
  57. int escape();
  58.  
  59. static int maxfds;
  60.  
  61. struct keymap {
  62.     char *keys;
  63.     char *action;
  64.     char fast;    /* if true, skip tty mode changing, spawn_id check,
  65.             etc */
  66. };
  67.  
  68. static char EmsgBadSpawnId[] = "bad spawn id (process died earlier?) - returning to script control";
  69. static char EmsgProcDied[] = "process died - returning to script control\r\n";
  70.  
  71. /* special pattern that signifies the expect interpreter itself */
  72. #define INTERPRETER_ACTION    "interpreter"
  73.  
  74. /* results of in_keymap */
  75. #define MATCH        1    /* first *match_length chars match */
  76. #define CAN_MATCH    2    /* might match in future, i.e., valid prefix */
  77. #define CANT_MATCH    3    /* the first *match_length chars can not */
  78.                 /* possibly match */
  79. /* This function accepts user keystrokes and returns one of the above values */
  80. /* describing whether the keystrokes match a key sequence, and could or */
  81. /* can't if more characters arrive */
  82. /* The function assigns a matching keymap if there is a match */
  83.  
  84. /* The basic idea of how this works is it does a smart sequential search. */
  85. /* It is optimized (if you can call it that) towards a small number of */
  86. /* key mappings, but still works well for large maps, since no function */
  87. /* calls are made, and we stop as soon as there is a single-char mismatch, */
  88. /* and go on to the next one.  A hash table or compiled DFA probably would */
  89. /* not buy very much here for most maps. */
  90. int
  91. in_keymap(keys,length,keymap,km_len,km_entry,match_length)
  92. char *keys;        /* characters actually read from stream */
  93. int length;        /* # of keys */
  94. struct keymap *keymap;    /* key map table */
  95. int km_len;        /* # of key mappings */
  96. struct keymap **km_entry;    /* key map that matched */
  97. int *match_length;    /* number of chars that matched */
  98. {
  99.     int i;        /* which keymap */
  100.     char *j;    /* which character in stream to test */
  101.     char *k;    /* which character in keymap to test */
  102.     char *last_char = keys+length;    /* actually one past last char */
  103.  
  104.     /* a tiny tweak that should help master output which typically */
  105.     /* is length and has no key maps.  This avoids the second half */
  106.     /* of this procedure, which otherwise mindlessly iterates on */
  107.     /* each character. */
  108.     if (km_len == 0) {
  109.         *match_length = length;
  110.         return(CANT_MATCH);
  111.     }
  112.         
  113.     for (i = 0;i<km_len;i++) {
  114.         for (j = keys, k=keymap[i].keys;; j++,k++) {
  115.         /* if we hit the end of this map, we must've matched! */
  116.         if (*k == 0) {
  117.             *km_entry = &keymap[i];
  118.             *match_length = j-keys;
  119.             return(MATCH);
  120.         }
  121.  
  122.         /* if we ran out of user-supplied characters, and still */
  123.         /* haven't matched, it might match if the user supplies more */
  124.         /* characters next time */
  125.         if (j == last_char) return(CAN_MATCH);
  126.  
  127.         /* skip to next key entry, if characters don't match */
  128.         if ((*j & 0x7f) != *k) break;
  129.         }
  130.         }
  131.  
  132.     /* at this point, we know that there are no matches beginning at the */
  133.     /* first character.  Start looking for characters that can be */
  134.     /* discarded.  We do this by comparing each incoming character to */
  135.     /* the first character of each keymap.  If equal, we return the */
  136.     /* number of characters skipped so far, and let us be called again */
  137.     /* to start the matching procedure at the beginning of the input. */
  138.  
  139.     /* can certainly skip 1st char, else would've matched above */
  140.     for (j=keys+1;j != last_char;j++) {
  141.         for (i=0;i<km_len;i++) {
  142.         /* if characters have arrived which don't match, but the */
  143.         /* next one might, return to let characters be flushed */
  144.             if (*j == keymap[i].keys[0]) {
  145.                 *match_length = j-keys;
  146.                 return(CANT_MATCH);
  147.             }
  148.         }
  149.     }
  150.  
  151.     *match_length = length;
  152.     return(CANT_MATCH);
  153. }
  154.  
  155. /* set things up for later calls to interact */
  156. void
  157. init_interact()
  158. {
  159. #ifdef HPUX
  160.     maxfds = sysconf(_SC_OPEN_MAX); /* from Ian Hogg, ian@dms.cdc.com */
  161. #else
  162.     maxfds = getdtablesize();
  163. #endif
  164. }
  165.  
  166. #define finish(x)    { status = x; goto done; }
  167.  
  168. /*ARGSUSED*/
  169. int
  170. cmdInteract(clientData, interp, argc, argv)
  171. ClientData clientData;
  172. Tcl_Interp *interp;
  173. int argc;
  174. char **argv;
  175. {
  176. #ifdef HPUX
  177.   fd_set excep;
  178.   int enable = 1;
  179. #endif
  180.   fd_set rdrs;
  181.   int rc, wc;            /* return codes for various system calls */
  182.  
  183.   struct keymap *ukey_map = 0;    /* user to process */
  184.   struct keymap *mkey_map = 0;    /* process to user */
  185.   struct keymap *km;        /* ptr for above while parsing */
  186.   int mkeys_mapped = 0;
  187.   int ukeys_mapped = 0;
  188.   int *keys_mapped = &ukeys_mapped;
  189.   int user_in = 0;        /* default stdin */
  190.   int user_out = 1;        /* default stdout */
  191.   int master;
  192.   struct f *m = 0, *u = 0;    /* pointers to user/master files */
  193.   int all_fast = FALSE;        /* by default, turn off -f */
  194.   char **oldargv = 0;        /* save original argv here if we split it */
  195.   int status = TCL_OK;        /* final return value */
  196.   int i;            /* trusty temp */
  197.   int user_match_in_progress = 0; /* true if CAN_MATCH was last result */
  198.   int master_match_in_progress = 0; /* ditto */
  199.   char *src, *dest;
  200.  
  201.   int tty_changed = FALSE;    /* true if we had to change tty modes for */
  202.   /* interact to work (i.e., to raw, noecho) */
  203.   int was_raw;
  204.   int was_echo;
  205.   exp_tty tty_old;
  206.  
  207.   int first_time = TRUE;    /* TRUE if we have not passed any characters
  208.                    between user and process in current call to cmdInteract.
  209.                    This is just to help with error messages.  Idea is that
  210.                    if the process is bad without sending a single character,
  211.                    then it was probably a mistake in the script.
  212.                    */
  213.  
  214.   int MasterIsTerminalP;    /* MDW: Mon Jun 22 14:53:41 1992 */
  215.   int omaster = -1;    /* MDW: Mon Jun 22 14:53:42 1992 */
  216.   int result;    /* MDW: Mon Jun 22 14:53:42 1992 */
  217.  
  218.   if (argc == 2 && strchr(argv[1],'\n')) {
  219.     return(exp_eval_with_one_arg(clientData,argc,argv));
  220.   }
  221.  
  222.   argv++;
  223.   argc--;
  224.  
  225.   if (argc != 0) {
  226.     /* If flags are used, this will be too large, but it's not */
  227.     /* worth the trouble of making two passes since free doesn't */
  228.     /* care, and we'll  adjust our own count later when we found */
  229.     /* out the real amount */
  230.     if (0 == (ukey_map = (struct keymap *)malloc(argc *
  231.                          sizeof(struct keymap)))) {
  232.       tcl_error("malloc failed (%d keymaps)",keys_mapped);
  233.       return(TCL_ERROR);
  234.     }
  235.     for (i=0;i<argc;i++) ukey_map[i].fast = FALSE;
  236.   }
  237.  
  238.   km = ukey_map;
  239.   for (;argc>0;argc--,argv++) {
  240.     if (streq(*argv,"-u")) {    /* treat process as user */
  241.       argc--;argv++;
  242.       if (argc < 1) {
  243.     tcl_error("interact: -u needs argument");
  244.     return(TCL_ERROR);
  245.       }
  246.       user_out = user_in = atoi(*argv);
  247.       continue;
  248.     } else if (streq(*argv,"-o")) { /* apply following patterns */
  249.       /* to opposite side of */
  250.       /* interaction */
  251.       if (mkey_map) {
  252.     tcl_error("interact: too many -o args");
  253.     return(TCL_ERROR);
  254.       }
  255.       /* give rest of user key map to master */
  256.       mkey_map = km;
  257.       keys_mapped = &mkeys_mapped;
  258.       continue;
  259.     } else if (streq(*argv,"-f")) {
  260.       if (argc < 1) {
  261.     tcl_error("interact: -f needs argument");
  262.     return(TCL_ERROR);
  263.       }
  264.       km->fast = TRUE;
  265.       continue;
  266.     } else if (streq(*argv,"-F")) {
  267.       all_fast = TRUE;
  268.       continue;
  269.     } else {
  270.       if (all_fast) km->fast = TRUE;
  271.       km->keys = *argv;
  272.       /* should really compare to f->umsize, but it's hard */
  273.       /* to get at this point */
  274.       if (BUFSIZ < strlen(km->keys)) {
  275.     tcl_error("key sequence \"%s\"> match_max (%s bytes)",km->keys,BUFSIZ);
  276.     finish(TCL_ERROR);
  277.       }
  278.       argc--;argv++;
  279.  
  280.       km->action = *argv;
  281.       debuglog("defining key %s, action %s\r\n",
  282.            km->keys,
  283.            km->action?(dprintify(km->action))
  284.            :INTERPRETER_ACTION);
  285.  
  286.       km++;
  287.       (*keys_mapped)++;
  288.     }
  289.   }
  290.  
  291.   debuglog("user_out = %d,  user_in = %d\r\n",user_out, user_in);
  292.  
  293.  restart:
  294.   if (0 == update_master(&master)) finish(TCL_ERROR);
  295.  
  296.   if (is_user(master)) {
  297.     tcl_error("cannot interact with self - set spawn_id to a spawned process");
  298.     finish(TCL_ERROR);
  299.   }
  300.  
  301.   if ((!(m = fd_to_f(master, "interact"))) || f_adjust(m) ||
  302.       (!(u = fd_to_f(user_in,"interact"))) || f_adjust(u)) {
  303.     finish(TCL_ERROR);
  304.   }
  305.  
  306.   MasterIsTerminalP = SetSessionFromFD(master);    /* MDW: Mon Jun 22 14:53:25 1992 */
  307.   if (omaster != master && MasterIsTerminalP) {    /* MDW: Mon Jun 22 14:53:27 1992 */
  308.     SetCurrSession();
  309.     ResetWindow();
  310.     omaster = master;
  311.   }    /* MDW: Mon Jun 22 14:53:29 1992 */
  312.  
  313.   /* if we are running this using /dev/tty */
  314. #define REALTTY (user_in == 0)
  315.   if (REALTTY) {
  316.     tty_changed = tty_raw_noecho(&tty_old,&was_raw,&was_echo);
  317.   }
  318.  
  319.   FD_ZERO(&rdrs);
  320. #ifdef HPUX
  321.   FD_ZERO(&excep);
  322.   /* Turn on trapping of close, open and ioctl requests from the slave.*/
  323.   ioctl(master, TIOCTRAP, &enable);
  324. #endif
  325.  
  326.   for (;;) {
  327.     int match;
  328.     int skip;            /* # of chars that have already been printed */
  329.     /* by expect and should be skipped */
  330.     int write_count;        /* true # of chars to write after taking */
  331.     int nmaster;    /* MDW: Mon Jun 22 14:54:39 1992 */
  332.     /* 'skip' into account */
  333.  
  334.     if (0 == update_master(&nmaster)) finish(TCL_ERROR);    /* MDW: Mon Jun 22 14:57:06 1992 */
  335.     if (nmaster != master) goto restart;    /* MDW: Mon Jun 22 14:57:08 1992 */
  336.  
  337.     if (((m->size == 0) && (u->size == 0)) || user_match_in_progress || master_match_in_progress) {
  338.       FD_SET(user_in,&rdrs); FD_SET(master,&rdrs);
  339.       if (MasterIsTerminalP && SessionBufferedInputP()) /* Session has cached input */
  340.     FD_CLR(user_in,&rdrs);
  341.       else {
  342.     /*debuglog("Calling select #1, %d, %ld\r\n", maxfds, rdrs.fds_bits[0]);*/
  343. #ifdef HPUX
  344.     FD_SET(user_in,&excep); FD_SET(master,&excep);
  345.     result = select(maxfds,&rdrs,(fd_set *)0,&excep,(struct timeval *)0);
  346. #else
  347.     result = select(maxfds,&rdrs,(fd_set *)0,(fd_set *)0,(struct timeval *)0);
  348. #endif
  349.     if (result == -1) {
  350.       /* window refreshes trigger EINTR, ignore */
  351.       if (errno == EINTR) continue;
  352.       if (errno != EBADF) {
  353.         /* not prepared to handle anything else */
  354.         errorlog("select: %s\r\n",sys_errlist[errno]);
  355.         bye(-1);
  356.       }
  357.       fd_close(master);
  358.       if (first_time) {
  359.         tcl_error(EmsgBadSpawnId);
  360.         finish(TCL_ERROR);
  361.       } else {
  362.         debuglog(EmsgProcDied);
  363.         finish(TCL_OK);
  364.       }
  365.     }
  366.       }
  367.     }
  368.  
  369.     if (m->size == 0 || master_match_in_progress) {
  370.       /* received from master, send to user */
  371.  
  372. #ifdef HPUX
  373.       /*
  374.        * Exceptional condition from slave process on pty.
  375.        * This could be either a close or an open or an ioctl
  376.        * request that is not handled by the termio driver on
  377.        * the slave side of the pty.
  378.        *
  379.        * On HP-UX this is the only way to tell that the other
  380.        * process exited.  We set up trapping above to trap
  381.        * close actions on the pty.  When the process on the
  382.        * slave side exits the file descriptor is closed.
  383.        * On BSD4.3 and Sys5 a read would return
  384.        * zero bytes read, so this code is not needed.
  385.        *
  386.        * This code provided by Bob Proulx and Jeff
  387.        * Okamoto of HP
  388.        */
  389.       if (FD_ISSET(master, &excep)) {
  390.  
  391.     struct request_info ioctl_info;
  392.     if (ioctl(master,TIOCREQCHECK,&ioctl_info) < 0) {
  393.       debuglog("ioctl error on TIOCREQCHECK: %d", errno);
  394.       break;
  395.     }
  396.     if (ioctl_info.request == TIOCCLOSE) {
  397.  
  398.       /* The process on the slave side closed the pty.  This probably means that it
  399.          exited.  It could be that the process just closed the file descriptor.
  400.          The close is not blocked on the slave side.  If the master side is closed
  401.          the handshake will be completed automatically.  Since there is no need to
  402.          complete the handshake we return here.
  403.          */
  404.       break;        /* EOF from slave process. */
  405.     }
  406.     /* Handshake the ioctl or the open request.  We don't know what the request
  407.        was but we return it as successfully completed even though we did nothing.
  408.        This was probably some type of modem control which would mean something on a
  409.        real tty but is meaningless on a pty.  The application is faked-out and that
  410.        is the purpose of this code anyway.
  411.        */
  412.     if (ioctl(master, TIOCREQSET, &ioctl_info) < 0)
  413.       debuglog("ioctl error on TIOCREQSET after ioctl or open on slave: %d", errno);
  414.       }
  415. #endif                /*HPUX*/
  416.  
  417.       /* received from master, send to user */
  418.       if (FD_ISSET(master, &rdrs)) {
  419.     /*NEW*/                
  420.     /* was rc = read(master, m->buffer, m->msize-m->size); */
  421.     char *buf = m->buffer;
  422.     int n, size = m->msize-m->size;
  423.     FD_CLR(master,&rdrs);
  424.     if (MasterIsTerminalP) rc = HandleSessionTerminal(buf, size, 0.0);
  425.     else rc = read(master, buf, size);
  426.     if (0 >= rc) {
  427.       debuglog("read(spawn_id=%d) = %d, errno = %d\r\n",master,rc,errno);
  428.       /* don't smash errno in prev. line */
  429.       fd_close(master);
  430.       if (first_time) {
  431.         tcl_error(EmsgBadSpawnId);
  432.         finish(TCL_ERROR);
  433.       } else {
  434.         debuglog(EmsgProcDied);
  435.         finish(TCL_OK);
  436.       }
  437.     }
  438.     buf[rc]='\0';
  439.     FD_CLR(master,&rdrs);
  440.  
  441.     /* got a char from master, so... */
  442.     first_time = FALSE;
  443.     m->size += rc;
  444.  
  445.     /* Process is sending us I/O.  Always send */
  446.     /* user but sometimes printify and surround */
  447.     /* with diagnostics. */
  448.     if (is_debugging || debugfile) {
  449.       int len; char *p;
  450.       debuglog("process typed {");
  451.       m->buffer[m->size] = 0;
  452.       p = printify(m->buffer);
  453.       len = strlen(p);
  454.       if (is_debugging) {
  455.         logn(p,len);
  456.         write(user_out,p,len);
  457.         if (user_out != 1)
  458.           write(1,p,len);
  459.       } else {
  460.         if (logfile)
  461.           fwrite(m->buffer,1,rc,logfile);
  462.         fwrite(p,1,len,debugfile);
  463.         write(user_out,m->buffer,rc);
  464.       }
  465.       /* want to send to both stdout/err */
  466.       /* but don't want user to see twice! */
  467.       debuglog("}\r\n");
  468.     }
  469.       }
  470.     }                /* else if (m->size == 0 */
  471.  
  472.     if (m->size != 0) {
  473.       int te;            /* result of Tcl_Eval */
  474.       struct keymap *map;
  475.  
  476.       master_match_in_progress = FALSE;
  477.       m->buffer[m->size]='\0';
  478.       switch (in_keymap(m->buffer,m->size,mkey_map,
  479.             mkeys_mapped,&map,&match)) {
  480.       case MATCH:
  481.     m->size -= match;
  482.  
  483.     /* handle chars left over from expect that */
  484.     /* shouldn't be printed again */
  485.     if (m->printed) {
  486.       if (m->printed > match) {
  487.         m->printed -= match;
  488.       } else  m->printed = 0;
  489.     }
  490.  
  491.     if (m->size != 0) {
  492.       memcpy(m->buffer,m->buffer+match,m->size);
  493.     }
  494.     m->buffer[m->size] = '\0';
  495.  
  496.     if (!map->fast && tty_changed)
  497.       tty_set(&tty_old,was_raw,was_echo);
  498.     if (!map->action) {
  499.       nflog("\r\n",1);
  500. /*      te = escape(interp); */
  501.       te = FuncallWithoutCurses(escape,1,interp);
  502.     } else {
  503.       te = Tcl_Eval(interp,map->action,0,(char **)0);
  504.     }
  505.     if (!map->fast && REALTTY) tty_changed =
  506.       tty_raw_noecho(&tty_old,&was_raw,&was_echo);
  507.     switch (te) {
  508.     case TCL_BREAK:
  509.     case TCL_CONTINUE:
  510.       finish(te);
  511.     case TCL_RETURN_TCL:
  512.       finish(TCL_RETURN);
  513.     case TCL_RETURN:
  514.       finish(TCL_OK);
  515.     case TCL_OK:
  516.       /* god knows what the user might */
  517.       /* have done to us in the way of */
  518.       /* closed fds, so .... */
  519.       if (map->fast) continue;
  520.       else goto restart;
  521.     default:
  522.       finish(TCL_ERROR);
  523.     }
  524.       case CAN_MATCH:
  525.     master_match_in_progress = TRUE;
  526.     /* force user keystrokes to be seen */
  527.     /* necessary, only because we check master's */
  528.     /* first, and if in the middle of a possible */
  529.     /* master match, logic above directs us here */
  530.     /*NEW*/                goto user_to_master;    /* was "continue"*/
  531.       case CANT_MATCH:
  532.     break;
  533.       }
  534.  
  535.       /* assert (case == CANT_MATCH) */
  536.  
  537.       m->buffer[m->size] = 0;
  538.  
  539.       /* If expect has left characters in buffer, it has already echoed them to the
  540.      screen, thus we must prevent them being rewritten.  Unfortunately this gives
  541.      the possibility of matching chars that have already been output, but we do so
  542.      since the user could have avoided it by flushing the output buffers directly.
  543.      Hopefully, this code should still be fast for when m->printed == 0
  544.      */
  545.       skip = 0;
  546.       write_count = match;
  547.       if (m->printed) {
  548.     skip = min(match,m->printed);
  549.     m->printed -= skip;
  550.     write_count -= skip;
  551.       }
  552.  
  553.       if (write_count) {    /* sorry about the formatting */
  554.     logn(m->buffer + skip,write_count);
  555.     if (MasterIsTerminalP) UpdateWindow(NULL); /* MDW: Mon Jun 22 17:03:15 1992 */
  556.     else {
  557.       wc = write(user_out,m->buffer + skip,write_count);
  558.       if (wc <= 0) {    /* user disappeared */
  559.         fd_close(user_out);
  560.         if (first_time) {
  561.           tcl_error("user died");
  562.           finish(TCL_ERROR);
  563.         } else {
  564.           debuglog("user went away or closed stdin\r\n");
  565.           finish(TCL_OK);
  566.         }
  567.       }
  568.     }
  569.       }
  570.       m->size -= match;
  571.       if (m->size != 0) {
  572.     memcpy(m->buffer,m->buffer+match,m->size);
  573.       }
  574.       m->buffer[m->size] = '\0';
  575.     }
  576.  
  577.     /*NEW*/user_to_master:
  578.     /* chars received from user, send to master */
  579.     if (u->size == 0 || user_match_in_progress) {
  580.       if (FD_ISSET(user_in, &rdrs)) {
  581.     if (0 >= (rc = read(user_in,    u->buffer + u->size,
  582.                 u->msize - u->size))) {
  583.       errorlog("user sent EOF or disappeared\r\n");
  584.       bye(-1);
  585.     }
  586.     /* avoid another function call if possible */
  587.     if (debugfile || is_debugging) {
  588.       u->buffer[u->size + rc] = 0;
  589.       debuglog("user typed {%s}\r\n",
  590.            printify(u->buffer + u->size));
  591.     }
  592.  
  593.     u->size += rc;
  594.     FD_CLR(user_in,&rdrs);
  595.       } else continue;
  596.     }
  597.  
  598.     if (u->size != 0) {
  599.       int te;            /* result of Tcl_Eval */
  600.       struct keymap *map;
  601.  
  602.       user_match_in_progress = FALSE;
  603.       u->buffer[u->size]='\0';
  604.       switch (in_keymap(u->buffer,u->size,ukey_map,
  605.             ukeys_mapped,&map,&match)) {
  606.       case MATCH:
  607.     u->size -= match;
  608.  
  609.     /* handle chars left over from expect that */
  610.     /* shouldn't be printed again */
  611.     if (u->printed) {
  612.       if (u->printed > match) {
  613.         u->printed -= match;
  614.       } else  u->printed = 0;
  615.     }
  616.  
  617.     if (u->size != 0) {
  618.       memcpy(u->buffer,u->buffer+match,u->size);
  619.     }
  620.     u->buffer[u->size] = '\0';
  621.  
  622.     if (!map->fast && tty_changed)
  623.       tty_set(&tty_old,was_raw,was_echo);
  624.     if (!map->action) {
  625.       nflog("\r\n",1);
  626.       te = FuncallWithoutCurses(escape,1,interp);
  627. /*      te = escape(interp); */
  628.     } else {
  629.       te = Tcl_Eval(interp,map->action,0,(char **)0);
  630.     }
  631.     if (!map->fast && REALTTY) tty_changed =
  632.       tty_raw_noecho(&tty_old,&was_raw,&was_echo);
  633.     switch (te) {
  634.     case TCL_BREAK:
  635.     case TCL_CONTINUE:
  636.       finish(te);
  637.     case TCL_RETURN_TCL:
  638.       finish(TCL_RETURN);
  639.     case TCL_RETURN:
  640.       finish(TCL_OK);
  641.     case TCL_OK:
  642.       /* god knows what the user might */
  643.       /* have done to us in the way of */
  644.       /* closed fds, so .... */
  645.       if (map->fast) continue;
  646.       else goto restart;
  647.     default:
  648.       finish(TCL_ERROR);
  649.     }
  650.       case CAN_MATCH:
  651.     user_match_in_progress = TRUE;
  652.     continue;
  653.       case CANT_MATCH:
  654.     break;
  655.       }
  656.  
  657.       /* assert (case == CANT_MATCH) */
  658.  
  659.       u->buffer[u->size] = 0;
  660.  
  661.       /* If expect has left characters in buffer, it has already echoed them */
  662.       /* to the screen, thus we must prevent them being rewritten.  Unfortunately */
  663.       /* this gives the possibility of matching chars that have already been */
  664.       /* output, but we do so since the user could have avoided it by flushing the */
  665.       /* output buffers directly. */
  666.       /* Hopefully, this code should still be fast for when m->printed == 0 */
  667.       skip = 0;
  668.       write_count = match;
  669.       if (u->printed) {
  670.     skip = min(match,u->printed);
  671.     u->printed -= skip;
  672.     write_count -= skip;
  673.       }
  674.  
  675.       if (write_count) {    /* sorry about the formatting */
  676.     wc = Write(master,u->buffer + skip,write_count);
  677.     *(u->buffer+skip+write_count) = '\0';
  678.     debuglog("INTERACT: Write(%d, \"%s\", %d) => %d\r\n", master, u->buffer+skip,wc, write_count);
  679.     if (wc <= 0) {        /* process disappeared */
  680.       fd_close(master);
  681.       if (first_time) {
  682.         tcl_error("process died");
  683.         finish(TCL_ERROR);
  684.       } else {
  685.         debuglog("process went away or closed stdin\r\n");
  686.         finish(TCL_OK);
  687.       }
  688.     }
  689.     first_time = FALSE;
  690.       }
  691.  
  692.       u->size -= match;
  693.       if (u->size > 0) {
  694.     memcpy(u->buffer,u->buffer+match,u->size);
  695.       }
  696.       u->buffer[u->size] = '\0';
  697.     }
  698.   }
  699.  done:
  700.   if (tty_changed) tty_set(&tty_old,was_raw,was_echo);
  701.   if (ukey_map) free((char *)ukey_map);
  702.   if (oldargv) free((char *)argv);
  703.  
  704.   /* restore lowercase buffer for expect */
  705.   /* do it here rather than upon entrance to expect because */
  706.   /* likelihood is that interact is called very infrequently */
  707.   /* and when called it rarely leaves any characters in buffers */
  708.   /* following code assumes buffers are always null-terminated */
  709.   /* which both expect and interact guarantee, and they are the only */
  710.   /* ones that ever manipulate buffer */
  711.   if (m && m->buffer) {
  712.     for (src = m->buffer, dest = m->lower;*src;src++,dest++) {
  713.       *dest = isupper(*src)?tolower(*src):*src;
  714.     }
  715.   }
  716.   if (u && u->buffer) {
  717.     for (src = u->buffer, dest = u->lower;*src;src++,dest++) {
  718.       *dest = isupper(*src)?tolower(*src):*src;
  719.     }
  720.   }
  721.  
  722.  
  723.   return(status);
  724. }
  725.  
  726. static struct timeval zerotime = {0, 0};
  727. static struct timeval anytime = {0, 0};    /* can be changed by user */
  728.  
  729. SafeSelect(maxfds, rdrs, ignore1, ignore2, intv)
  730. int maxfds;
  731. fd_set *rdrs, *ignore1, *ignore2;
  732. struct timeval *intv;
  733. {
  734.   int res, n;
  735.   extern double TimevalToDouble(), TimeOfDay();
  736.   struct timeval tv_struct, *tv = (intv ? &tv_struct : NULL);
  737.   double before, after, delta = 0.0;
  738.   fd_set nrdrs;
  739.  
  740. /*  
  741.   debuglog("EXPECTERM:  SafeSelect(%d, %x, %x, %x, %f)",
  742.        maxfds, 
  743.        (rdrs ? (int)rdrs->fds_bits[0] : 0),
  744.        (ignore1 ? (int)ignore1->fds_bits[0] : 0),
  745.        (ignore2 ? (int)ignore2->fds_bits[0] : 0),
  746.        TimevalToDouble(intv));
  747. */
  748.   if (tv) { 
  749.     before = TimeOfDay(); 
  750.     tv->tv_sec = intv->tv_sec;
  751.     tv->tv_usec = intv->tv_usec;
  752.   }
  753.   FD_ZERO(&nrdrs);
  754.   for (n=0; n < maxfds; ++n) if (FD_ISSET(n, rdrs)) FD_SET(n, &nrdrs);
  755.   while(1) {
  756.     res = select(maxfds,rdrs,(fd_set *)0,(fd_set *)0,tv);
  757.     if (-1 == res) break;
  758.     for (n=0; n < maxfds; ++n) if (FD_ISSET(n, rdrs)) break;
  759.     if (n < maxfds) break;
  760.     if (!tv) continue; /* should have waited forever */
  761.     after = TimeOfDay();
  762.     delta = TimevalToDouble(tv) - (after - before);
  763.     if (delta > 0.0) {
  764.       before = after;
  765.       FD_ZERO(rdrs);
  766.       for (n=0; n < maxfds; ++n) if (FD_ISSET(n, &nrdrs)) FD_SET(n, rdrs);
  767.       DoubleToTimeval(delta, tv);
  768.     }
  769.     else break;
  770. /*    debuglog("[RETRY sec: %f, res: %d, errno: %d]", delta, res, errno); */
  771.   }
  772. /*  debuglog(" => %d, rdrs=%x, delta=%f, errno: %d\n",
  773.        res,
  774.        nrdrs.fds_bits[0],
  775.        delta,
  776.        errno);
  777. */
  778.   return(res);
  779. }
  780.  
  781.  
  782. /* returns TCL_OK or TCL_ERROR */
  783. /* if TCL_OK && *master2 && (*n2 == 0), then timed out */
  784. int
  785. ready(masters,n,masters2,n2, tv)
  786. int *masters;
  787. int n;
  788. int *masters2;    /* out variable */
  789. int *n2;    /* out variable */
  790. struct timeval *tv;
  791. {
  792.   int i;            /* index into in-array */
  793.   int j;            /* index into out-array */
  794.   int selectp = 1;    /* MDW: Mon Jun 22 17:39:59 1992 */
  795.   fd_set rdrs;
  796.   struct timeval *t = (tv && (tv->tv_sec > 0 || tv->tv_usec > 0)) ? tv : NULL;
  797.  
  798.  restart:
  799.   FD_ZERO(&rdrs);
  800.   for (i = 0;i < n;i++) {
  801.     FD_SET(masters[i],&rdrs);
  802.     if (SetSessionFromFD(masters[i]) && CheckInput(masters[i],0.0,TRUE)) selectp = 0;
  803.   }
  804.  
  805.   if (!selectp) {
  806.     FD_ZERO(&rdrs);
  807.     for (i = 0;i < n;i++) {
  808.       if (SetSessionFromFD(masters[i]) && CheckInput(masters[i],0.0,TRUE)) FD_SET(masters[i],&rdrs);
  809.     }
  810.   }
  811.   else if (-1 == SafeSelect(maxfds,&rdrs,(fd_set *)0,(fd_set *)0,t)) {
  812.     /* window refreshes trigger EINTR, ignore */
  813.     if (errno == EINTR) goto restart;
  814.     else if (errno == EBADF) {
  815.       /* someone is rotten */
  816.       for (i=0;i<n;i++) {
  817.     fd_set suspect;
  818.     FD_ZERO(&suspect);
  819.     FD_SET(masters[i],&suspect);
  820.     if (-1 == SafeSelect(maxfds,&suspect,
  821.              (fd_set *)0,(fd_set *)0,&zerotime)) {
  822.       tcl_error("invalid spawn_id (%d)\r",masters[i]);
  823.       return(TCL_ERROR);
  824.     }
  825.       }
  826.     } else {
  827.       /* not prepared to handle anything else */
  828.       tcl_error("select: %s\r",sys_errlist[errno]);
  829.       return(TCL_ERROR);
  830.     }
  831.   }
  832.  
  833.   j=0;
  834.   for (i=0;i<n;i++) {
  835.     if (FD_ISSET(masters[i],&rdrs)) {
  836.       masters2[j] = masters[i];
  837.       if (TCL_ERROR == f_adjust(masters2[j] + fs))
  838.     return(TCL_ERROR);
  839.       j++;
  840.       if (j==*n2) break;
  841.     }
  842.   }
  843.   *n2 = j;            /* returns number of fds set */
  844.   if (tv) tv->tv_sec = tv->tv_usec = 0;
  845.   return(TCL_OK);
  846. }
  847.  
  848. void
  849. usleep(usec)
  850. long usec;        /* microseconds */
  851. {
  852.     struct timeval t;
  853.  
  854.     t.tv_sec = usec/1000000L;
  855.     t.tv_usec = usec%1000000L;
  856.     (void) select(1, (fd_set *)0, (fd_set *)0, (fd_set *)0, &t);
  857. }
  858.